﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;

namespace MineSweeperChallenge
{
    public class MultiThreadedGameRunner : IGameRunner
    {
        private BackgroundWorker mBackgroundWorker;
        private object[][] mGameObjects;
        private ManualResetEvent mDoneEvent;
        private int mGamesToDo;

        public void RunGame(ISweeper pSweeper, int pTimes, int pWidth, int pHeight, int pMines, Action<string> pCallbackAction, ProgressBar pProgressBar)
        {
            mBackgroundWorker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true };

            mBackgroundWorker.DoWork += (sender, e) =>
            {
                mGamesToDo = pTimes;

                Game.Result[] lGameResults = new Game.Result[pTimes];
                mDoneEvent = new ManualResetEvent(false);
                mGameObjects = new object[pTimes][];

                for (int i = 0; i < pTimes; i++)
                {
                    mGameObjects[i] = new object[] { pSweeper, pWidth, pHeight, pMines, lGameResults, i };
                    ThreadPool.QueueUserWorkItem(new WaitCallback(PlayGame), i);
                }

                try
                {
                    mDoneEvent.WaitOne();

                    e.Result = string.Format("Times played: {0}\nTimes won: {1}\nAverage execution time (milliseconds): {2}\nTimes ran with errors: {3}", pTimes, lGameResults.Count(pGameResult => pGameResult.Win), lGameResults.Select(pGameResult => pGameResult.TimeTakenMilliseconds).Average(), lGameResults.Count(pGameResult => pGameResult.HadException));
                }
                catch (Exception exc)
                {
                    e.Result = exc.Message;
                }
            };
            mBackgroundWorker.RunWorkerCompleted += (sender, e) =>
            {
                if (pCallbackAction != null) pCallbackAction((string)e.Result);
            };

            mBackgroundWorker.ProgressChanged += (sender, e) =>
            {
                pProgressBar.Dispatcher.BeginInvoke(DispatcherPriority.Render, (SendOrPostCallback)delegate { pProgressBar.SetValue(ProgressBar.ValueProperty, e.ProgressPercentage / 100d); }, null);
            };

            mBackgroundWorker.RunWorkerAsync();
        }

        private void PlayGame(object pParams)
        {
            object[] lParams = mGameObjects[(int) pParams];
            PlayGame((ISweeper) lParams[0], (int) lParams[1], (int) lParams[2], (int) lParams[3], (Game.Result[]) lParams[4], (int) lParams[5]);
            int mPercentage = 100 - Convert.ToInt32((Convert.ToDouble(mGamesToDo - 1) / mGameObjects.Length) * 100);
            mBackgroundWorker.ReportProgress(mPercentage);
            if (Interlocked.Decrement(ref mGamesToDo) == 0)
            {
                mDoneEvent.Set();
            }
        }

        private void PlayGame(ISweeper pSweeper, int pWidth, int pHeight, int pMines, Game.Result[] pGameResults, int i)
        {
            Game lGame = new Game(pSweeper, pWidth, pHeight, pMines);
            lGame.Initialize();
            pGameResults[i] = lGame.Play();
            Thread.Sleep(1);
        }
    }
}
